package studio.raptor.ddal.core.engine.plan.node.impl.index;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import studio.raptor.ddal.common.collections.FastArrayList;
import studio.raptor.ddal.config.model.shard.Index;
import studio.raptor.ddal.config.model.shard.Table;
import studio.raptor.ddal.core.engine.IndexTableOps;
import studio.raptor.ddal.core.engine.ProcessContext;
import studio.raptor.ddal.core.engine.plan.node.ProcessNode;
import studio.raptor.ddal.core.parser.result.SQLStatementType;

/**
 * 构造IUD类型Index执行SQL
 *
 * @author Charley
 * @since 1.0
 */
public class BuildIUDIndexSql extends ProcessNode {

  @Override
  protected void execute(ProcessContext context) {
    String sql = context.getStatementContext().getOriginSql();
    SQLStatementType sqlType = context.getStatementContext().getParseResult().getSqlType();
    List<Object> parameters = context.getStatementContext().getSqlParameters();
    Collection<Index> indexTable = getIndexTable(context);
    List<IndexTableOps> opsList = assembleIndexOps(sql, sqlType, parameters, indexTable);
    context.getStatementContext().getIndexTableOpsList().addAll(opsList);
  }

  /**
   * 获取需要处理的索引信息
   */
  private Collection<Index> getIndexTable(ProcessContext context) {
    Collection<Index> result = Collections.EMPTY_LIST;
    int count = 0;
    for (Table shardTable : context.getStatementContext().getShardTables()) {
      if (shardTable.hasIndex()) {
        result = shardTable.getIndexColumn().values();
        count++;
      }
    }

    if (count > 1) {
      throw new UnsupportedOperationException(
          "Could not deal with more than one table contain index.");
    }

    return result;
  }

  private List<IndexTableOps> assembleIndexOps(String sql, SQLStatementType sqlType,
      List<Object> parameters, Collection<Index> indexTable) {

    StringPosition position;
    switch (sqlType) {
      case UPDATE:
        position = new UpdateLexer(sql).lex();
        break;
      case INSERT:
        position = new InsertLexer(sql).lex();
        break;
      case DELETE:
        position = new DeleteLexer(sql).lex();
        break;
      default:
        throw new UnsupportedOperationException();
    }

    List<IndexTableOps> opsList = new FastArrayList<>();
    for (Index index : indexTable) {
      IndexTableOps ops=null;
      if(index.isCopy()){
        ops = new IndexTableOps();
        ops.getParameters().addAll(parameters);
        ops.setIdxSql(sql.replace(sql.substring(position.begin, position.end), index.getName()));
      }
      else{
        String idxSql=sql.replace(sql.substring(position.begin, position.end), index.getName());
        switch (sqlType) {
          case UPDATE:
            ops=assembleIndexOpsOfUpdate(idxSql.toUpperCase(),parameters,index);
            break;
          case INSERT:
            ops=assembleIndexOpsOfInsert(idxSql.toUpperCase(),parameters,index);
            break;
          case DELETE:
            ops=assembleIndexOpsOfDelete(idxSql.toUpperCase(),parameters,index);
            break;
          default:
            throw new UnsupportedOperationException();
        }
      }
      if(ops!=null){
        opsList.add(ops);
      }
    }
    return opsList;
  }

  /**
   * 构造新增操作-索引表语句
   * @param sql
   * @param parameters
   * @param index
   * @return
   */
  private IndexTableOps assembleIndexOpsOfInsert(String sql,List<Object> parameters,Index index){
    int colIndex=sql.indexOf( "VALUES");
    String originSqlSuffix=sql.substring( colIndex + 6 );
    originSqlSuffix=originSqlSuffix.substring( originSqlSuffix.indexOf( "(" )+1,originSqlSuffix.indexOf( ")" ) );
    String originSqlPrefix=sql.substring( sql.indexOf( "(" )+1, colIndex);
    originSqlPrefix=originSqlPrefix.substring( 0,originSqlPrefix.indexOf( ")" ));
    String idxTbName=index.getName();
    String originSql=sql.substring( 0,sql.indexOf( idxTbName )+idxTbName.length());
//    String columnsStr=index.getColumnsStr();
//    originSql=originSql+"("+columnsStr+") VALUES(";
    originSql=originSql+"(";
    List<String> idxColumns=index.getColumns();
    int[] paraIndex=new int[idxColumns.size()];
    int i=0;
    for(String column:idxColumns){
      int indexT=originSqlPrefix.indexOf(column.toUpperCase());
      if(indexT== -1 ){
        continue;
      }
      int indexPara=0;
      if(indexT !=0 ){
        String columnsStrPrefix=originSqlPrefix.substring( 0, indexT);
        indexPara=calcSubStrNum(columnsStrPrefix,",");
      }
      paraIndex[i++]=indexPara;
      originSql=originSql+column+",";
    }
    originSql=originSql.substring( 0,originSql.length()-1 )+") VALUES(";

    List<Object> sqlParameters = new FastArrayList<>();
    String[] values=originSqlSuffix.split( "," );
    for(int j=0; j<paraIndex.length;j++){
      int m=0;
      int n=0;
      for( ;n<paraIndex[j] ; n++){
        if(!values[n].trim().equals( "?" )) m++;
      }
      if(values[paraIndex[j]].trim().equals( "?" )){
        sqlParameters.add( parameters.get( paraIndex[j]-m ) );
        originSql=originSql+"?,";
      }
      else{
        originSql=originSql+values[paraIndex[j]].trim()+",";
      }
    }
    originSql=originSql.substring( 0,originSql.length()-1 )+")";

    IndexTableOps ops = new IndexTableOps();
    ops.getParameters().addAll(sqlParameters);
    ops.setIdxSql(originSql);
    ops.setCopy( false );
    return ops;
  }

  /**
   * 构造更新操作-索引表语句
   * @param sql
   * @param parameters
   * @param index
   * @return
   */
  private IndexTableOps assembleIndexOpsOfUpdate(String sql,List<Object> parameters,Index index){
    int colIndex=sql.indexOf( "WHERE");
    String originSqlSuffix=sql.substring( colIndex + 6 );
    String originSqlPrefix=sql.substring( sql.indexOf( "SET" )+4, colIndex).trim();
    String originSql=sql.substring( 0, sql.indexOf( "SET" )+4);
    List<Object> sqlParameters = new FastArrayList<>();
    String[] values=originSqlPrefix.split( "," );
    String columnStr=index.getColumnsStr().toUpperCase();
    int j=0;
    int m=0;
    boolean hasColumn=false;
    for(j=0; j<values.length;j++) {
      String[] columnObjStr = values[j].split( "=" );
      if (columnStr.indexOf( columnObjStr[0].trim() ) != -1) {
        if(hasColumn){
          originSql = originSql + ","+values[j];
        }
        else{
          originSql = originSql + values[j];
          hasColumn=true;
        }
        if ("?".equals( columnObjStr[1].trim() )) {
          sqlParameters.add( parameters.get( j - m ) );
        }else{
          m++;
        }
      }else {
        if (!"?".equals( columnObjStr[1].trim())) m++;
      }
    }
    originSql=originSql+" WHERE ";
    //todo:OR?
    values=originSqlSuffix.split( "AND" );
    boolean hasCondition=false;
    for(int i=0; i<values.length;i++) {
      String[] columnObjStr = values[i].split( "=" );
      if (columnStr.indexOf( columnObjStr[0].trim()) != -1) {
        if(hasCondition){
          originSql = originSql + " and "+values[i];
        }
        else{
          originSql = originSql + values[i];
          hasCondition = true ;
        }
        if ("?".equals( columnObjStr[1].trim() )) {
          sqlParameters.add( parameters.get( i+j - m ) );
        }else{
          m++;
        }
      } else {
        if (!"?".equals( columnObjStr[1].trim())) m++;
      }
    }
    IndexTableOps ops = new IndexTableOps();
    if(hasColumn && hasCondition ){
      ops.getParameters().addAll(sqlParameters);
      ops.setIdxSql(originSql);
      ops.setCopy( false );
      return ops;
    }
    return null;
  }

  /**
   * 构造删除操作-索引表语句
   * @param sql
   * @param parameters
   * @param index
   * @return
   */
  private IndexTableOps assembleIndexOpsOfDelete(String sql,List<Object> parameters,Index index){
    int colIndex=sql.indexOf( "WHERE");
    String originSqlSuffix=sql.substring( colIndex + 6 );
    String idxTbName=index.getName();
    String originSql=sql.substring( 0,sql.indexOf( idxTbName )+idxTbName.length());

    List<Object> sqlParameters = new FastArrayList<>();
    String columnStr=index.getColumnsStr().toUpperCase();
    int m=0;
    originSql=originSql+" WHERE ";
    //todo:OR?
    String[] values=originSqlSuffix.split( "AND" );
    boolean hasCondition=false;
    for(int i=0; i<values.length;i++) {
      String[] columnObjStr = values[i].split( "=" );
      if (columnStr.indexOf( columnObjStr[0].trim()) != -1) {
        if(hasCondition){
          originSql = originSql + " and "+values[i];
        }
        else{
          originSql = originSql + values[i];
          hasCondition = true ;
        }
        if ("?".equals( columnObjStr[1].trim() )) {
          sqlParameters.add( parameters.get( i - m ) );
        }else{
          m++;
        }
      } else {
        if (!"?".equals( columnObjStr[1].trim())) m++;
      }
    }
    IndexTableOps ops = new IndexTableOps();
    if(hasCondition ){
      ops.getParameters().addAll(sqlParameters);
      ops.setIdxSql(originSql);
      ops.setCopy( false );
      return ops;
    }
    return null;
  }

  /**
   * 统计子串出现的次数
   * @param string
   * @param subStr
   * @return
   */
  public int calcSubStrNum(String string, String subStr) {
    int i = string.length() - string.replace(subStr, "").length();
    return  i / subStr.length();
  }

  private static abstract class Lexer {

    protected final String sql;
    protected final int length;
    protected int index = 0;
    protected char ch;
    protected boolean isStarted = false;

    protected Lexer(String sql) {
      this.sql = sql;
      this.length = sql.length();
    }

    protected void reader() {
      ch = sql.charAt(index++);
    }

    public StringPosition lex() {
      StringPosition position = new StringPosition();

      for (; ; ) {
        reader();
        if (index >= length) {
          break;
        }

        if (isStarted && ch != ' ') {
          position.begin = index - 1;
          for(;;){
            reader();
            if (' ' == ch || '(' == ch) {
              break;
            }
          }
          position.end = index - 1;
          break;
        }

        readKey();
      }
      return position;
    }

    abstract void readKey();

    protected String readString() {
      StringBuffer sb = new StringBuffer().append(ch);
      for (; ; ) {
        if (index >= length) {
          break;
        }
        reader();
        if (' ' == ch) {
          break;
        }
        sb.append(ch);
      }
      return sb.toString();
    }
  }

  private static class InsertLexer extends Lexer {

    public InsertLexer(String sql) {
      super(sql);
    }

    @Override
    public void readKey() {
      if (ch == 'I' || ch == 'i') {
        String key = readString();
        if (key.toUpperCase().equals("INTO")) {
          isStarted = true;
        }
      }
    }
  }

  private static class UpdateLexer extends Lexer {

    public UpdateLexer(String sql) {
      super(sql);
    }

    @Override
    public void readKey() {
      if (ch == 'U' || ch == 'u') {
        String key = readString();
        if (key.toUpperCase().equals("UPDATE")) {
          isStarted = true;
        }
      }
    }
  }

  private static class DeleteLexer extends Lexer {

    public DeleteLexer(String sql) {
      super(sql);
    }

    @Override
    public void readKey() {
      if (ch == 'F' || ch == 'f') {
        String key = readString();
        if (key.toUpperCase().equals("FROM")) {
          isStarted = true;
        }
      }
    }
  }

  public static class StringPosition {
    private int begin = 0;
    private int end = 0;
  }
}
